﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ArgusLib.Threading.Collections
{
	public interface ISynchronizedEnumerable<T> : IEnumerable<T>
	{
		object SyncRoot { get; }
		ISynchronizedEnumerator<T> GetEnumerator();
		void Lock();
		void Unlock();
		bool TryLock();
	}

	public abstract class SynchronizedEnumerableBase<T> : ISynchronizedEnumerable<T>
	{
		public object SyncRoot { get; private set; }

		public SynchronizedEnumerableBase()
			: this(new object()) { }

		public SynchronizedEnumerableBase(object syncRoot)
		{
			if (syncRoot == null)
				throw new ArgumentNullException("syncRoot");
			this.SyncRoot = syncRoot;
		}

		public void Lock() { Monitor.Enter(this.SyncRoot); }
		public void Unlock() { Monitor.Exit(this.SyncRoot); }
		public bool TryLock() { return Monitor.TryEnter(this.SyncRoot); }

		protected abstract ISynchronizedEnumerator<T> GetEnumeratorCore();
		ISynchronizedEnumerator<T> ISynchronizedEnumerable<T>.GetEnumerator()
		{
			ISynchronizedEnumerator<T> enumerator = this.GetEnumeratorCore();
			if (enumerator.SyncRoot != this.SyncRoot)
				throw new InvalidSyncRootException();
			return enumerator;
		}
		IEnumerator<T> IEnumerable<T>.GetEnumerator()
		{
			ISynchronizedEnumerator<T> enumerator = this.GetEnumeratorCore();
			if (enumerator.SyncRoot != this.SyncRoot)
				throw new InvalidSyncRootException();
			return enumerator;
		}
		IEnumerator IEnumerable.GetEnumerator()
		{
			ISynchronizedEnumerator<T> enumerator = this.GetEnumeratorCore();
			if (enumerator.SyncRoot != this.SyncRoot)
				throw new InvalidSyncRootException();
			return enumerator;
		}
	}
}
